package cn.zf233.xcloud.service.impl;

import cn.zf233.xcloud.commom.Const;
import cn.zf233.xcloud.commom.R;
import cn.zf233.xcloud.config.lockpool.IXCloudThreadPool;
import cn.zf233.xcloud.entity.File;
import cn.zf233.xcloud.entity.Share;
import cn.zf233.xcloud.entity.User;
import cn.zf233.xcloud.mapper.FileMapper;
import cn.zf233.xcloud.mapper.ShareMapper;
import cn.zf233.xcloud.mapper.UserMapper;
import cn.zf233.xcloud.service.ShareService;
import cn.zf233.xcloud.util.CommonUtil;
import cn.zf233.xcloud.util.OSSUtil;
import cn.zf233.xcloud.util.QRCodeUtil;
import cn.zf233.xcloud.util.ThreadSafeDateUtil;
import cn.zf233.xcloud.vo.ShareVo;
import com.aliyun.oss.OSS;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.framework.AopContext;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;

import javax.annotation.Resource;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;

/**
 * Created by zf233 on 2021/2/14
 * 分享服务
 */
@Service
public class ShareServiceImpl implements ShareService {

    private final Logger logger = LoggerFactory.getLogger(ShareServiceImpl.class);

    @Resource
    private ShareMapper shareMapper;

    @Resource
    private FileMapper fileMapper;

    @Resource
    private UserMapper userMapper;

    @Resource
    private OSSUtil ossUtil;

    @Resource
    private QRCodeUtil qrCodeUtil;

    @Resource
    private IXCloudThreadPool<Lock> lockPool;

    @Override
    public R<String> shareFileDec(User user, Long fileId, Integer type, Integer daysOfTimeout) { // type: 0-二维码 1-链接 daysOfTimeout-有效期
        if (userMapper.selectByPrimaryKey(user.getId()) == null) {
            return R.createByErrorIllegalArgument("登陆失效，请重新登录");
        }

        Lock lock = lockPool.getShareLock(user.getId());
        try {
            if (lock.tryLock(3, TimeUnit.SECONDS)) {
                return ((ShareServiceImpl) AopContext.currentProxy()).shareFile(user, fileId, type, daysOfTimeout);
            }
        } catch (InterruptedException e) {
            this.logger.error(" *****SHARE FILE LOCK**** EXCEPTION INTERRUPTED  --------> " + e.getMessage());
        } finally {
            lockPool.unLock(lock);
        }
        return R.createByErrorMessage("服务器繁忙，请稍后再试");
    }

    @Transactional(rollbackFor = Exception.class)
    public R<String> shareFile(User user, Long fileId, Integer type, Integer daysOfTimeout) { // type: 0-二维码 1-链接 daysOfTimeout-有效期
        User userInfo = userMapper.selectByPrimaryKey(user.getId());

        // 文件所属用户存在
        if (userInfo == null) {
            return R.createByErrorIllegalArgument("登陆失效，请重新登录");
        }

        File fileInfo = fileMapper.selectByPrimaryKey(fileId);

        // 文件存在
        if (fileInfo == null) {
            return R.createByErrorMessage("分享文件不存在");
        }
        if (fileInfo.isDeleted()) {
            return R.createByErrorMessage("废纸篓内文件不支持分享");
        }
        if (userInfo.getShareNumber() <= 0) {
            return R.createByErrorMessage("用户当日分享次数已经用尽");
        }

        long now = ThreadSafeDateUtil.now();
        Long shareId = fileInfo.getShareId();
        Share shareInfo;
        if (shareId != -1) {
            shareInfo = shareMapper.selectByPrimaryKey(shareId);
        } else {
            shareInfo = new Share();
            shareInfo.setUserId(userInfo.getId());
            shareInfo.setFileId(fileId);
            int resultShare = shareMapper.insert(shareInfo);
            fileInfo.setShareId(shareInfo.getId());
            int resultFile = fileMapper.updateByPrimaryKeySelective(fileInfo);
            if (resultShare <= 0 || resultFile <= 0) {
                TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
                return R.createByErrorMessage("文件分享失败");
            }
        }
        if (!(daysOfTimeout == 3 || daysOfTimeout == 7)) { // 非3、7天默认为3天
            daysOfTimeout = 3;
        }
        if (!(type == 0 || type == 1)) { // 非二维码、链接方式默认链接
            type = 1;
        }
        StringBuilder builder = new StringBuilder();
        Random random = new Random();
        for (int i = 0; i < 6; i++) {
            builder.append(random.nextInt(10));
        }
        String link;
        String code = builder.toString();
        String qrLink = null;
        if (type == 0) { // 获取二维码

            // 创建一个包含分享链接（Restful格式）的二维码并上传至oss获取二维码地址
            qrLink = createQrFile(Const.FILE_SHARE_LINK_PREFIX + userInfo.getId() + "/" + fileId + "/" + type + "/" + code);
            if (StringUtils.isBlank(qrLink)) {
                return R.createByErrorMessage("获取失败");
            }
            if (StringUtils.isNotBlank(shareInfo.getQrLink())) {
                OSS ossClient = ossUtil.getOSSClient();
                if (ossUtil.objectNameExists(ossClient, shareInfo.getQrLink())) {
                    ossUtil.delete(ossClient, shareInfo.getQrLink());
                }
                ossClient.shutdown();
            }
            shareInfo.setQrLink(qrLink);
            shareInfo.setQrLinkTime(now);
            shareInfo.setQrLinkTimeout(TimeUnit.DAYS.toMillis(daysOfTimeout));
            shareInfo.setQrLinkCheckCode(code);
            link = Const.SHARE_QR_REQUEST_URL_PREFIX + shareInfo.getQrLink();
        } else {
            shareInfo.setShareLink(Const.FILE_SHARE_LINK_PREFIX + userInfo.getId() + "/" + fileId + "/" + type + "/" + code);
            shareInfo.setShareLinkTime(now);
            shareInfo.setShareLinkTimeout(TimeUnit.DAYS.toMillis(daysOfTimeout));
            shareInfo.setShareLinkCheckCode(code);
            link = shareInfo.getShareLink();
        }

        int resultShare = shareMapper.updateByPrimaryKeySelective(shareInfo);
        userInfo.setShareNumber(userInfo.getShareNumber() - 1);
        int resultUser = userMapper.updateByPrimaryKeySelective(userInfo);
        if (resultShare > 0 && resultUser > 0 && StringUtils.isNotBlank(link)) {
            return R.createBySuccess(link);
        }
        if (type == 0 && StringUtils.isNotBlank(qrLink)) {
            OSS ossClient = ossUtil.getOSSClient();
            if (ossUtil.objectNameExists(ossClient, qrLink)) {
                ossUtil.delete(ossClient, qrLink);
            }
            ossClient.shutdown();
        }
        TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        return R.createByErrorMessage("获取失败");
    }

    @Override
    public R timeout(Long fileId, Long userId, Integer type, String code) {
        File fileInfo = fileMapper.selectByPrimaryKey(fileId);
        if (fileInfo == null) {
            return R.createByErrorMessage("分享文件不存在");
        }
        if (fileInfo.isAFolder() || !(type == 0 || type == 1)) {
            return R.createByErrorMessage("分享链接或二维码解析有误");
        }
        if (!fileInfo.getUserId().equals(userId)) {
            return R.createByErrorMessage("分享链接或二维码解析有误");
        }
        if (fileInfo.isDeleted()) {
            return R.createByErrorMessage("分享链接已失效");
        }

        Share shareInfo = shareMapper.selectByPrimaryKey(fileInfo.getShareId());
        if (shareInfo != null) { // 分享链接存在
            long now = ThreadSafeDateUtil.now();
            if (type == 0) {
                if (StringUtils.isBlank(shareInfo.getQrLink())) {
                    return R.createByErrorMessage("分享二维码解析有误");
                }
                if (now - shareInfo.getQrLinkTime() > shareInfo.getQrLinkTimeout()) {
                    return R.createByErrorMessage("分享二维码已失效");
                }
                if (shareInfo.getQrLinkCheckCode().equals(code)) {
                    return R.createBySuccess(shareInfo.getQrLink());
                }
                return R.createByErrorMessage("分享二维码解析有误");
            } else {
                if (StringUtils.isBlank(shareInfo.getShareLink())) {
                    return R.createByErrorMessage("分享链接解析有误");
                }
                if (now - shareInfo.getShareLinkTime() > shareInfo.getShareLinkTimeout()) {
                    return R.createByErrorMessage("分享链接已失效");
                }
                if (shareInfo.getShareLinkCheckCode().equals(code)) {
                    return R.createBySuccess(shareInfo.getShareLink());
                }
                return R.createByErrorMessage("分享链接解析有误");
            }
        }
        if (type == 0) {
            return R.createByErrorMessage("不存在的分享二维码");
        } else {
            return R.createByErrorMessage("不存在的分享链接");
        }
    }

    @Override
    public R<List<ShareVo>> listShare(User user) {
        List<Share> shareList = shareMapper.selectByUserId(user.getId());
        return R.createBySuccess(assembleFileVoDetail(shareList));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public R remove(Long[] shareId) {
        if (shareId == null || shareId.length == 0) {
            return R.createByErrorMessage("未指定要移除的文件分享");
        }

        int count = 0;
        for (Long id : shareId) {
            Share shareInfo = shareMapper.selectByPrimaryKey(id);
            if (shareInfo == null) {
                continue;
            }
            File fileInfo = fileMapper.selectByPrimaryKey(shareInfo.getFileId());
            if (fileInfo == null) {
                int shareResult = shareMapper.deleteByPrimaryKey(id);
                if (shareResult > 0) {
                    if (StringUtils.isNotBlank(shareInfo.getQrLink())) {
                        removeQrFile(shareInfo.getQrLink());
                    }
                    count++;
                }
            } else {
                fileInfo.setShareId(-1L);
                int fileResult = fileMapper.updateByPrimaryKeySelective(fileInfo);
                int shareResult = shareMapper.deleteByPrimaryKey(id);
                if (fileResult > 0 && shareResult > 0) {
                    if (StringUtils.isNotBlank(shareInfo.getQrLink())) {
                        removeQrFile(shareInfo.getQrLink());
                    }
                    count++;
                }
            }
        }
        return R.createBySuccessMessage(count + "个文件分享移除成功");
    }

    @Override
    public R<ShareVo> getFileShare(User user, Long fileId) {
        User userInfo = userMapper.selectByPrimaryKey(user.getId());
        if (userInfo == null) {
            return R.createByErrorIllegalArgument("登陆失效，请重新登录");
        }
        File fileInfo = fileMapper.selectByPrimaryKey(fileId);
        if (fileInfo == null) {
            return R.createByErrorIllegalArgument("文件不存在");
        }
        if (fileInfo.isDeleted()) {
            return R.createByErrorMessage("废纸篓文件不支持获取分享");
        }
        if (fileInfo.getShareId() == -1) {
            return R.createByErrorMessage("此文件未分享");
        }
        Share shareInfo = shareMapper.selectByPrimaryKey(fileInfo.getShareId());
        if (shareInfo != null) {
            List<Share> shareList = new ArrayList<>();
            shareList.add(shareInfo);
            List<ShareVo> shareVoList = assembleFileVoDetail(shareList);
            if (!shareVoList.isEmpty()) {
                return R.createBySuccess("获取成功", shareVoList.get(0));
            }
        }
        return R.createByErrorMessage("获取文件分享失败");
    }

    @Override
    public R extend(Long shareId, Integer type) {
        long now = ThreadSafeDateUtil.now();
        if (type == 0 || type == 1) {
            Share shareInfo = shareMapper.selectByPrimaryKey(shareId);
            if (shareInfo == null) {
                return R.createByErrorMessage("此分享不存在");
            }
            if (type == 0) {
                if (StringUtils.isBlank(shareInfo.getQrLink())) {
                    return R.createByErrorMessage("未生成分享二维码");
                }
                long currentTimeout = shareInfo.getQrLinkTimeout() - (now - shareInfo.getQrLinkTime());
                if (currentTimeout > 0L) {
                    if (TimeUnit.MILLISECONDS.toHours(currentTimeout) >= 720L) {
                        return R.createByErrorMessage("已达到分享最大有效期");
                    }
                    shareInfo.setQrLinkTimeout(shareInfo.getQrLinkTimeout() + TimeUnit.DAYS.toMillis(3L));
                } else {
                    shareInfo.setQrLinkTimeout(now - shareInfo.getQrLinkTime() + TimeUnit.DAYS.toMillis(3L));
                }
                int result = shareMapper.updateByPrimaryKeySelective(shareInfo);
                if (result > 0) {
                    return R.createBySuccessMessage("分享二维码有效期延长成功");
                }
                return R.createBySuccessMessage("分享二维码有效期延长失败");
            } else {
                if (StringUtils.isBlank(shareInfo.getShareLink())) {
                    return R.createByErrorMessage("未生成分享链接");
                }
                long currentTimeout = shareInfo.getShareLinkTimeout() - (now - shareInfo.getShareLinkTime());
                if (currentTimeout > 0L) {
                    if (TimeUnit.MILLISECONDS.toHours(currentTimeout) >= 720L) {
                        return R.createByErrorMessage("已达到分享最大有效期");
                    }
                    shareInfo.setShareLinkTimeout(shareInfo.getShareLinkTimeout() + TimeUnit.DAYS.toMillis(3L));
                } else {
                    shareInfo.setShareLinkTimeout(now - shareInfo.getShareLinkTime() + TimeUnit.DAYS.toMillis(3L));
                }
                int result = shareMapper.updateByPrimaryKeySelective(shareInfo);
                if (result > 0) {
                    return R.createBySuccessMessage("分享链接有效期延长成功");
                }
                return R.createBySuccessMessage("分享链接有效期延长失败");
            }
        }
        return R.createByErrorMessage("文件分享类型参数有误");
    }

    private List<ShareVo> assembleFileVoDetail(List<Share> shareList) {
        List<ShareVo> shareVoList = new ArrayList<>(shareList.size());
        long now = ThreadSafeDateUtil.now();
        for (Share share : shareList) {
            File file = fileMapper.selectByPrimaryKey(share.getFileId());
            if (file == null) {
                continue;
            }

            ShareVo shareVo = new ShareVo();
            if (StringUtils.isNotBlank(share.getQrLink())) {
                shareVo.setQrLink(Const.SHARE_QR_REQUEST_URL_PREFIX + share.getQrLink());
            }
            if (StringUtils.isNotBlank(share.getShareLink())) {
                shareVo.setShareLink(share.getShareLink());
            }
            shareVo.setId(share.getId());
            shareVo.setFileId(share.getFileId());
            shareVo.setFileName(file.getOldFileName());

            if (StringUtils.isNotBlank(share.getQrLink())) {
                if ((share.getQrLinkTimeout() - TimeUnit.SECONDS.toMillis(5)) > (now - share.getQrLinkTime())) {
                    shareVo.setQrTimeout(CommonUtil.timeoutSwitch(share.getQrLinkTimeout() - (now - share.getQrLinkTime())));
                    shareVo.setQrLinkStatus(1);
                } else {
                    shareVo.setQrTimeout("已失效");
                    shareVo.setQrLinkStatus(-1);
                }
            } else {
                shareVo.setQrTimeout("未创建");
                shareVo.setQrLinkStatus(0);
            }
            if (StringUtils.isNotBlank(share.getShareLink())) {
                if ((share.getShareLinkTimeout() - TimeUnit.SECONDS.toMillis(5)) > (now - share.getShareLinkTime())) {
                    shareVo.setLinkTimeout(CommonUtil.timeoutSwitch(share.getShareLinkTimeout() - (now - share.getShareLinkTime())));
                    shareVo.setShareLinkStatus(1);
                } else {
                    shareVo.setLinkTimeout("已失效");
                    shareVo.setShareLinkStatus(-1);
                }
            } else {
                shareVo.setLinkTimeout("未创建");
                shareVo.setShareLinkStatus(0);
            }
            shareVoList.add(shareVo);
        }
        return shareVoList;
    }

    private String createQrFile(String content) {

        // 迁移至OSS 为了保证响应速度，logo地址仍然选择本地
        /*try {

            return qrCodeUtil.createAndUpload(content,
                    Const.SHARE_QR_LOGO_REAL_PATH,
                    true,
                    false,
                    QR_SHARE_FILE_NAME,
                    (fileName, inputStream) -> {

                        OSS ossClient = ossUtil.getOSSClient();
                        String randomFileName = ossUtil.getRandomFileName(fileName);
                        ossUtil.upload(ossClient, randomFileName, fileName, inputStream);
                        ossClient.shutdown();

                        return randomFileName;
                    });
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;*/

        // 部署服务器动静分离
        try {
            final String qrFileName = Const.SHARE_QR_FILE_NAME_PREFIX + UUID.randomUUID() + ".jpg";
            java.io.File file = new java.io.File(Const.SHARE_QR_STRONGER_REAL_PATH, qrFileName);
            FileOutputStream out = new FileOutputStream(file);
            qrCodeUtil.create(content, Const.SHARE_QR_LOGO_CLASS_PATH, out);
            return qrFileName;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    private void removeQrFile(String name) {
        java.io.File file = new java.io.File(Const.SHARE_QR_STRONGER_REAL_PATH, name);
        if (!file.exists()) {
            logger.info("此 ( {} ) 分享二维码不存在", name);
        }
        boolean flag = file.delete();
        if (flag) {
            logger.info("此 ( {} ) 分享二维码移除成功", name);
        } else {
            logger.info("此 ( {} ) 分享二维码移除失败", name);
        }
    }
}
